<!DOCTYPE html>
<!-- saved from url=(0118)http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7 -->
<html lang="en" dir="ltr" class="client-js"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Complete Roguelike Tutorial, using python+libtcod, part 7 - RogueBasin</title>
<meta charset="UTF-8">
<meta name="generator" content="MediaWiki 1.19.2">
<link rel="shortcut icon" href="http://roguebasin.roguelikedevelopment.org/favicon.ico">
<link rel="search" type="application/opensearchdescription+xml" href="http://roguebasin.roguelikedevelopment.org/opensearch_desc.php" title="RogueBasin (en)">
<link rel="EditURI" type="application/rsd+xml" href="http://roguebasin.roguelikedevelopment.org/api.php?action=rsd">
<link rel="alternate" type="application/atom+xml" title="RogueBasin Atom feed" href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:RecentChanges&feed=atom">
<link rel="stylesheet" href="http://roguebasin.roguelikedevelopment.org/load.php?debug=false&lang=en&modules=mediawiki.legacy.commonPrint%2Cshared%7Cskins.monobook&only=styles&skin=monobook&*">
<!--[if IE 6]><link rel="stylesheet" href="/skins/monobook/IE60Fixes.css?303" media="screen" /><![endif]-->
<!--[if IE 7]><link rel="stylesheet" href="/skins/monobook/IE70Fixes.css?303" media="screen" /><![endif]--><style type="text/css" media="all">.js-messagebox{margin:1em 5%;padding:0.5em 2.5%;border:1px solid #ccc;background-color:#fcfcfc;font-size:0.8em}.js-messagebox .js-messagebox-group{margin:1px;padding:0.5em 2.5%;border-bottom:1px solid #ddd}.js-messagebox .js-messagebox-group:last-child{border-bottom:thin none transparent}

/* cache key: 133099-basin:resourceloader:filter:minify-css:7:8b08bdc91c52a9ffba396dccfb5b473c */


.mw-collapsible-toggle{float:right} li .mw-collapsible-toggle{float:none} .mw-collapsible-toggle-li{list-style:none}

/* cache key: 133099-basin:resourceloader:filter:minify-css:7:4250852ed2349a0d4d0fc6509a3e7d4c */
</style><meta name="ResourceLoaderDynamicStyles" content="">
<link rel="stylesheet" href="http://roguebasin.roguelikedevelopment.org/load.php?debug=false&lang=en&modules=site&only=styles&skin=monobook&*">
<style>a:lang(ar),a:lang(ckb),a:lang(fa),a:lang(kk-arab),a:lang(mzn),a:lang(ps),a:lang(ur){text-decoration:none}a.new,#quickbar a.new{color:#ba0000}

/* cache key: 133099-basin:resourceloader:filter:minify-css:7:c88e2bcd56513749bec09a7e29cb3ffa */
</style>

<script src="./Complete Roguelike Tutorial, using python+libtcod, part 7 - RogueBasin_files/load.php"></script><style type="text/css"></style><script src="./Complete Roguelike Tutorial, using python+libtcod, part 7 - RogueBasin_files/load(1).php"></script>
<script>if(window.mw){
mw.config.set({"wgCanonicalNamespace":"","wgCanonicalSpecialPageName":false,"wgNamespaceNumber":0,"wgPageName":"Complete_Roguelike_Tutorial,_using_python+libtcod,_part_7","wgTitle":"Complete Roguelike Tutorial, using python+libtcod, part 7","wgCurRevisionId":30255,"wgArticleId":3814,"wgIsArticle":true,"wgAction":"view","wgUserName":null,"wgUserGroups":["*"],"wgCategories":["Developing"],"wgBreakFrames":false,"wgPageContentLanguage":"en","wgSeparatorTransformTable":["",""],"wgDigitTransformTable":["",""],"wgRelevantPageName":"Complete_Roguelike_Tutorial,_using_python+libtcod,_part_7","wgRestrictionEdit":[],"wgRestrictionMove":[]});
}</script><script>if(window.mw){
mw.loader.implement("user.options",function($){mw.user.options.set({"ccmeonemails":0,"cols":80,"date":"default","diffonly":0,"disablemail":0,"disablesuggest":0,"editfont":"default","editondblclick":0,"editsection":1,"editsectiononrightclick":0,"enotifminoredits":0,"enotifrevealaddr":0,"enotifusertalkpages":1,"enotifwatchlistpages":0,"extendwatchlist":0,"externaldiff":0,"externaleditor":0,"fancysig":0,"forceeditsummary":0,"gender":"unknown","hideminor":0,"hidepatrolled":0,"highlightbroken":1,"imagesize":2,"justify":0,"math":1,"minordefault":0,"newpageshidepatrolled":0,"nocache":0,"noconvertlink":0,"norollbackdiff":0,"numberheadings":0,"previewonfirst":0,"previewontop":1,"quickbar":5,"rcdays":7,"rclimit":50,"rememberpassword":0,"rows":25,"searchlimit":20,"showhiddencats":0,"showjumplinks":1,"shownumberswatching":1,"showtoc":1,"showtoolbar":1,"skin":"monobook","stubthreshold":0,"thumbsize":2,"underline":2,"uselivepreview":0,"usenewrc":0,"watchcreations":0,"watchdefault":0,"watchdeletion":
0,"watchlistdays":3,"watchlisthideanons":0,"watchlisthidebots":0,"watchlisthideliu":0,"watchlisthideminor":0,"watchlisthideown":0,"watchlisthidepatrolled":0,"watchmoves":0,"wllimit":250,"variant":"en","language":"en","searchNs0":true,"searchNs1":false,"searchNs2":false,"searchNs3":false,"searchNs4":false,"searchNs5":false,"searchNs6":false,"searchNs7":false,"searchNs8":false,"searchNs9":false,"searchNs10":false,"searchNs11":false,"searchNs12":false,"searchNs13":false,"searchNs14":false,"searchNs15":false});;},{},{});mw.loader.implement("user.tokens",function($){mw.user.tokens.set({"editToken":"+\\","watchToken":false});;},{},{});

/* cache key: 133099-basin:resourceloader:filter:minify-js:7:74a832f2292f1f4d40d425d223444e78 */
}</script>
<script>if(window.mw){
mw.loader.load(["mediawiki.page.startup","mediawiki.legacy.wikibits","mediawiki.legacy.ajax"]);
}</script><script type="text/javascript" src="./Complete Roguelike Tutorial, using python+libtcod, part 7 - RogueBasin_files/load(2).php"></script>
<style type="text/css">/*<![CDATA[*/
.source-python {line-height: normal;}
.source-python li, .source-python pre {
	line-height: normal; border: 0px none white;
}
/**
 * GeSHi Dynamically Generated Stylesheet
 * --------------------------------------
 * Dynamically generated stylesheet for python
 * CSS class: source-python, CSS id: 
 * GeSHi (C) 2004 - 2007 Nigel McNie, 2007 - 2008 Benny Baumann
 * (http://qbnz.com/highlighter/ and http://geshi.org/)
 * --------------------------------------
 */
.python.source-python .de1, .python.source-python .de2 {font: normal normal 1em/1.2em monospace; margin:0; padding:0; background:none; vertical-align:top;}
.python.source-python  {font-family:monospace;}
.python.source-python .imp {font-weight: bold; color: red;}
.python.source-python li, .python.source-python .li1 {font-weight: normal; vertical-align:top;}
.python.source-python .ln {width:1px;text-align:right;margin:0;padding:0 2px;vertical-align:top;}
.python.source-python .li2 {font-weight: bold; vertical-align:top;}
.python.source-python .kw1 {color: #ff7700;font-weight:bold;}
.python.source-python .kw2 {color: #008000;}
.python.source-python .kw3 {color: #dc143c;}
.python.source-python .kw4 {color: #0000cd;}
.python.source-python .co1 {color: #808080; font-style: italic;}
.python.source-python .coMULTI {color: #808080; font-style: italic;}
.python.source-python .es0 {color: #000099; font-weight: bold;}
.python.source-python .br0 {color: black;}
.python.source-python .sy0 {color: #66cc66;}
.python.source-python .st0 {color: #483d8b;}
.python.source-python .nu0 {color: #ff4500;}
.python.source-python .me1 {color: black;}
.python.source-python .ln-xtra, .python.source-python li.ln-xtra, .python.source-python div.ln-xtra {background-color: #ffc;}
.python.source-python span.xtra { display:block; }

/*]]>*/
</style>
<style type="text/css">/*<![CDATA[*/
@import "/index.php?title=MediaWiki:Geshi.css&usemsgcache=yes&action=raw&ctype=text/css&smaxage=18000";
/*]]>*/
</style></head>
<body class="mediawiki ltr sitedir-ltr ns-0 ns-subject page-Complete_Roguelike_Tutorial_using_python_libtcod_part_7 skin-monobook action-view">
<div id="globalWrapper">
<div id="column-content"><div id="content"><div id="mw-js-message" class="js-messagebox" style="display: none;"></div>
	<a id="top"></a>
	<div id="siteNotice"><div id="localNotice" lang="en" dir="ltr"></div></div>
	<h1 id="firstHeading" class="firstHeading"><span dir="auto">Complete Roguelike Tutorial, using python+libtcod, part 7</span></h1>
	<div id="bodyContent" class="mw-body">
		<div id="siteSub">From RogueBasin</div>
		<div id="contentSub"></div>
		<div id="jump-to-nav" class="mw-jump">Jump to: <a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7#column-one">navigation</a>, <a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7#searchInput">search</a></div>
		<!-- start content -->
<div id="mw-content-text" lang="en" dir="ltr" class="mw-content-ltr"><center><table border="0" cellpadding="10" cellspacing="0" style="background:#F0E68C"><tbody><tr><td><center>
<p>This is part of a series of tutorials; the main page can be found <a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod" title="Complete Roguelike Tutorial, using python+libtcod">here</a>.
</p><p>The tutorial uses libctod version 1.5.1. If you prefer to use 1.5.0, you can find the old version <a rel="nofollow" class="external text" href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7&oldid=29873">here</a>
</p>
</center></td></tr></tbody></table></center>
<table id="toc" class="toc"><tbody><tr><td><div id="toctitle"><h2>Contents</h2><span class="toctoggle">&nbsp;[<a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7#" class="internal" id="togglelink">hide</a>]&nbsp;</span></div>
<ul>
<li class="toclevel-1"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7#The_GUI"><span class="tocnumber">1</span> <span class="toctext"><b>The GUI</b></span></a>
<ul>
<li class="toclevel-2 tocsection-1"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7#Status_bars"><span class="tocnumber">1.1</span> <span class="toctext">Status bars</span></a></li>
<li class="toclevel-2 tocsection-2"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7#The_message_log"><span class="tocnumber">1.2</span> <span class="toctext">The message log</span></a></li>
<li class="toclevel-2 tocsection-3"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7#Mouse-look"><span class="tocnumber">1.3</span> <span class="toctext">Mouse-look</span></a></li>
</ul>
</li>
</ul>
</td></tr></tbody></table>
<p><br>
</p>
<center><h1> <span class="mw-headline" id="The_GUI"><b>The GUI</b></span></h1></center>
<p><br>
</p>
<h2> <span class="mw-headline" id="Status_bars"> Status bars </span></h2>
<p>Lots of stuff happens under the hood of a game that players don't really appreciate, like the combat mechanics detailed in the last couple of sections. We'll now work on something much more flashy -- the Graphical User Interface! Using the full power of libtcod's true-color consoles, and a bit of creativity, you can make some truly amazing graphics. You may argue that the limitations of a console actually make it easier to create a polished game, rather than if you had the freedom to position per-pixel graphics like most other games.
</p><p>We'll start by creating a GUI panel at the bottom of the screen. Of course, you're welcome to change this to suit your taste. For now, it will hold the player's health bar and a colored message log.
</p><p>It's easier to manage GUI windows and panels with an off-screen console for each one, created before the main loop:
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">panel = libtcod.<span class="me1">console_new</span><span class="br0">(</span>SCREEN_WIDTH, PANEL_HEIGHT<span class="br0">)</span></pre></div></div></div>
<p><br>
The constant <i>PANEL_HEIGHT</i> is defined later, along with others. Let's jump right to the "status bar" rendering code! This is fully generic and can be used for experience bars, mana bars, recharge times, dungeon level, you name it.
</p><p>The bar has two parts, one rectangle that changes size according to the proportion between the value and the maximum value, and a background rectangle. It just takes a simple formula to calculate that size, and a few calls to libtcod's <i>console_rect</i> function for the rectangles.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1"><span class="kw1">def</span> render_bar<span class="br0">(</span>x, y, total_width, name, value, maximum, bar_color, back_color<span class="br0">)</span>:
    <span class="co1">#render a bar (HP, experience, etc). first calculate the width of the bar</span>
    bar_width = <span class="kw2">int</span><span class="br0">(</span><span class="kw2">float</span><span class="br0">(</span>value<span class="br0">)</span> / maximum <span class="sy0">*</span> total_width<span class="br0">)</span>
&nbsp;
    <span class="co1">#render the background first</span>
    libtcod.<span class="me1">console_set_default_background</span><span class="br0">(</span>panel, back_color<span class="br0">)</span>
    libtcod.<span class="me1">console_rect</span><span class="br0">(</span>panel, x, y, total_width, <span class="nu0">1</span>, <span class="kw2">False</span>, libtcod.<span class="me1">BKGND_SCREEN</span><span class="br0">)</span>
&nbsp;
    <span class="co1">#now render the bar on top</span>
    libtcod.<span class="me1">console_set_default_background</span><span class="br0">(</span>panel, bar_color<span class="br0">)</span>
    <span class="kw1">if</span> bar_width <span class="sy0">&gt;</span> <span class="nu0">0</span>:
        libtcod.<span class="me1">console_rect</span><span class="br0">(</span>panel, x, y, bar_width, <span class="nu0">1</span>, <span class="kw2">False</span>, libtcod.<span class="me1">BKGND_SCREEN</span><span class="br0">)</span></pre></div></div></div>
<p><br>
For extra clarity, the actual value and maximum are displayed as text over the bar, along with a caption ('Health', 'Mana', etc).
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">    <span class="co1">#finally, some centered text with the values</span>
    libtcod.<span class="me1">console_set_default_foreground</span><span class="br0">(</span>panel, libtcod.<span class="me1">white</span><span class="br0">)</span>
    libtcod.<span class="me1">console_print_ex</span><span class="br0">(</span>panel, x + total_width / <span class="nu0">2</span>, y, libtcod.<span class="me1">BKGND_NONE</span>, libtcod.<span class="me1">CENTER</span>,
        name + <span class="st0">': '</span> + <span class="kw2">str</span><span class="br0">(</span>value<span class="br0">)</span> + <span class="st0">'/'</span> + <span class="kw2">str</span><span class="br0">(</span>maximum<span class="br0">)</span><span class="br0">)</span></pre></div></div></div>
<p><br>
Now we'll modify the main rendering function to use this. First, define a few constants: the height of the panel, its position on the screen (it's a bottom panel so only the Y is needed) and the size of the health bar.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1"><span class="co1">#sizes and coordinates relevant for the GUI</span>
BAR_WIDTH = <span class="nu0">20</span>
PANEL_HEIGHT = <span class="nu0">7</span>
PANEL_Y = SCREEN_HEIGHT - PANEL_HEIGHT</pre></div></div></div>
<p><br>
I also changed <i>MAP_HEIGHT</i> to 43 to give the panel more room. At the end of <i>render_all</i>, replace the code that shows the player's stats as text with the following code. It re-initializes the panel to black, calls our <i>render_bar</i> function to display the player's health, then shows the panel on the root console.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">    <span class="co1">#prepare to render the GUI panel</span>
    libtcod.<span class="me1">console_set_default_background</span><span class="br0">(</span>panel, libtcod.<span class="me1">black</span><span class="br0">)</span>
    libtcod.<span class="me1">console_clear</span><span class="br0">(</span>panel<span class="br0">)</span>
&nbsp;
    <span class="co1">#show the player's stats</span>
    render_bar<span class="br0">(</span><span class="nu0">1</span>, <span class="nu0">1</span>, BAR_WIDTH, <span class="st0">'HP'</span>, player.<span class="me1">fighter</span>.<span class="me1">hp</span>, player.<span class="me1">fighter</span>.<span class="me1">max_hp</span>,
        libtcod.<span class="me1">light_red</span>, libtcod.<span class="me1">darker_red</span><span class="br0">)</span>
&nbsp;
    <span class="co1">#blit the contents of "panel" to the root console</span>
    libtcod.<span class="me1">console_blit</span><span class="br0">(</span>panel, <span class="nu0">0</span>, <span class="nu0">0</span>, SCREEN_WIDTH, PANEL_HEIGHT, <span class="nu0">0</span>, <span class="nu0">0</span>, PANEL_Y<span class="br0">)</span></pre></div></div></div>
<p><br>
Time to test it -- that health bar looks pretty sweet! And you can easily make more like it with different colors and all.
</p><p>A small detail, the console where the map is rendered (<i>con</i>) should be the size of the map, not the size of the screen. This is more noticeable now that the panel takes up quite a bit of space. Change <i>SCREEN_WIDTH</i> and <i>SCREEN_HEIGHT</i> to <i>MAP_WIDTH</i> and <i>MAP_HEIGHT</i> when creating this console and blitting it. It's the <i>con = libtcod.console_new(...)</i> line before the main loop, and the first <i>console_blit</i> in <i>render_all</i>.
</p><p><br>
</p>
<h2> <span class="mw-headline" id="The_message_log"> The message log </span></h2>
<p>Until now the combat messages were dumped in the standard console -- not very user-friendly. We'll make a nice scrolling message log embedded in the GUI panel, and use colored messages so the player can know what happened with a single glance. It will also feature word-wrap!
</p><p>The constants that define the message bar's position and size are:
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">MSG_X = BAR_WIDTH + <span class="nu0">2</span>
MSG_WIDTH = SCREEN_WIDTH - BAR_WIDTH - <span class="nu0">2</span>
MSG_HEIGHT = PANEL_HEIGHT - <span class="nu0">1</span></pre></div></div></div>
<p><br>
This is so it appears to the right of the health bar, and fills up the rest of the space. The messages will be stored in a list so they can be easily manipulated. Each message is a tuple with 2 fields: the message string, and its color.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1"><span class="co1">#create the list of game messages and their colors, starts empty</span>
game_msgs = <span class="br0">[</span><span class="br0">]</span></pre></div></div></div>
<p><br>
A simple-to-use function will handle adding messages to the list. It will use Python's textwrap module to split a message into several lines if it's too long! For that, put <i>import textwrap</i> at the top of the file, and create the function:
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1"><span class="kw1">def</span> message<span class="br0">(</span>new_msg, color = libtcod.<span class="me1">white</span><span class="br0">)</span>:
    <span class="co1">#split the message if necessary, among multiple lines</span>
    new_msg_lines = <span class="kw3">textwrap</span>.<span class="me1">wrap</span><span class="br0">(</span>new_msg, MSG_WIDTH<span class="br0">)</span>
&nbsp;
    <span class="kw1">for</span> line <span class="kw1">in</span> new_msg_lines:
        <span class="co1">#if the buffer is full, remove the first line to make room for the new one</span>
        <span class="kw1">if</span> <span class="kw2">len</span><span class="br0">(</span>game_msgs<span class="br0">)</span> == MSG_HEIGHT:
            <span class="kw1">del</span> game_msgs<span class="br0">[</span><span class="nu0">0</span><span class="br0">]</span>
&nbsp;
        <span class="co1">#add the new line as a tuple, with the text and the color</span>
        game_msgs.<span class="me1">append</span><span class="br0">(</span> <span class="br0">(</span>line, color<span class="br0">)</span> <span class="br0">)</span></pre></div></div></div>
<p><br>
After obtaining the broken up message as a list of strings, it adds them one at a time to the actual message log. This is so that, when the log gets full, the first line is removed to make space for the new line. Dealing with one line at a time makes it easy to ensure that the message log never has more than a maximum height.
</p><p>The code to show the message log is simpler. Just loop through the lines and print them with the appropriate colors (right before rendering the health bar). Notice how we get the values of the tuple right in the for loop; this sort of feature in Python (called <i>unpacking</i>) allows you to write very concise code.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">    <span class="co1">#print the game messages, one line at a time</span>
    y = <span class="nu0">1</span>
    <span class="kw1">for</span> <span class="br0">(</span>line, color<span class="br0">)</span> <span class="kw1">in</span> game_msgs:
        libtcod.<span class="me1">console_set_default_foreground</span><span class="br0">(</span>panel, color<span class="br0">)</span>
        libtcod.<span class="me1">console_print_ex</span><span class="br0">(</span>panel, MSG_X, y, libtcod.<span class="me1">BKGND_NONE</span>, libtcod.<span class="me1">LEFT</span>, line<span class="br0">)</span>
        y += <span class="nu0">1</span></pre></div></div></div>
<p><br>
Ready to test! Let's print a friendly message before the main loop to welcome the player to our dungeon of doom:
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1"><span class="co1">#a warm welcoming message!</span>
message<span class="br0">(</span><span class="st0">'Welcome stranger! Prepare to perish in the Tombs of the Ancient Kings.'</span>, libtcod.<span class="me1">red</span><span class="br0">)</span></pre></div></div></div>
<p><br>
The long message allows us to test the word-wrap. You can now replace all the calls to the standard <i>print</i> with calls to our own <i>message</i> function (all 4 of them). I made the player death message red (<i>libtcod.red</i>), and the monster death message orange (<i>libtcod.orange</i>), others are the default. By the way, <a rel="nofollow" class="external text" href="http://doryen.eptalys.net/data/libtcod/doc/1.5.1/html2/color.html?c=false&cpp=false&cs=false&py=true&lua=false">here's the list of standard libtcod colors</a>. It's very handy, if you don't mind using a pre-defined palette of colors!
</p><p>This is just the kind of polish that our game needed, it's much more attractive, even for casual players. Don't let the die-hard roguelike players fool you, everyone likes a little bit of eye candy!
</p><p><br>
</p>
<h2> <span class="mw-headline" id="Mouse-look"> Mouse-look </span></h2>
<p>We'll now work some interactivity into our GUI. Roguelikes have a long tradition of using strict keyboard interfaces, and that's nice; but for a couple of tasks, like selecting a tile, a mouse interface is much easier. So we'll implement something like a "look" command, by automatically showing the name of any object the player hovers the mouse with! You could also use it for selecting targets of spells and ranged combat. Of course this is only a tutorial, showing you what you <i>can</i> do, and you may decide to replace this with a traditional "look" command!
</p><p>Using libtcod it's very easy to know the position of the mouse, and if there were any clicks: the <i>sys_check_for_event</i> function returns information on both keyboard and mouse activity: (<a rel="nofollow" class="external text" href="http://doryen.eptalys.net/data/libtcod/doc/1.5.1/html2/console_non_blocking_input.html?c=false&cpp=false&cs=false&py=true&lua=false">see the docs</a> for more details, and <a rel="nofollow" class="external text" href="http://doryen.eptalys.net/data/libtcod/doc/1.5.1/html2/mouse.html?c=false&cpp=false&cs=false&py=true&lua=false">here</a> for the mouse structure). 
</p><p>We need to restructure the program a little bit to use this combined mouse and keyboard detection. Just before the main loop, add
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">mouse = libtcod.<span class="me1">Mouse</span><span class="br0">(</span><span class="br0">)</span>
key = libtcod.<span class="me1">Key</span><span class="br0">(</span><span class="br0">)</span></pre></div></div></div>
<p>and just after the main loop begins, but before the call to <i>render_all</i>, add
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">    libtcod.<span class="me1">sys_check_for_event</span><span class="br0">(</span>libtcod.<span class="me1">EVENT_KEY_PRESS</span>|libtcod.<span class="me1">EVENT_MOUSE</span>,key,mouse<span class="br0">)</span></pre></div></div></div>
<p>Now, declare <i>keys</i> as a global in <i>handle_keys</i>, and remove the line which calls <i>libtcod.console_check_for_keypress</i>.
</p><p>We'll access the <i>cx</i> and <i>cy</i> fields of the mouse structure, which are the coordinates of the tile (or cell) that the mouse is over. This goes in a new function, that is supposed to return a string with the names of objects under the mouse:
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1"><span class="kw1">def</span> get_names_under_mouse<span class="br0">(</span><span class="br0">)</span>:
    <span class="kw1">global</span> mouse
&nbsp;
    <span class="co1">#return a string with the names of all objects under the mouse</span>
    <span class="br0">(</span>x, y<span class="br0">)</span> = <span class="br0">(</span>mouse.<span class="me1">cx</span>, mouse.<span class="me1">cy</span><span class="br0">)</span></pre></div></div></div>
<p><br>
Now we need to gather a list of names of objects that satisfy a few conditions: they're under the mouse, and inside the player's FOV. (Otherwise he or she would be able to detect enemies through walls!) This can be done with list comprehensions, <a rel="nofollow" class="external text" href="http://docs.python.org/tutorial/datastructures.html#list-comprehensions">using the "if" variant</a>.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">    <span class="co1">#create a list with the names of all objects at the mouse's coordinates and in FOV</span>
    names = <span class="br0">[</span>obj.<span class="me1">name</span> <span class="kw1">for</span> obj <span class="kw1">in</span> objects
        <span class="kw1">if</span> obj.<span class="me1">x</span> == x <span class="kw1">and</span> obj.<span class="me1">y</span> == y <span class="kw1">and</span> libtcod.<span class="me1">map_is_in_fov</span><span class="br0">(</span>fov_map, obj.<span class="me1">x</span>, obj.<span class="me1">y</span><span class="br0">)</span><span class="br0">]</span></pre></div></div></div>
<p><br>
After that mouthful, it's a simple matter of joining the names into a single string, using commas. Python has neat functions for this and capitalizing the first letter:
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">    names = <span class="st0">', '</span>.<span class="me1">join</span><span class="br0">(</span>names<span class="br0">)</span>  <span class="co1">#join the names, separated by commas</span>
    <span class="kw1">return</span> names.<span class="me1">capitalize</span><span class="br0">(</span><span class="br0">)</span></pre></div></div></div>
<p><br>
The function <i>render_all</i> can call this to get the string that depends on the mouse's position, after rendering the health bar:
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">    <span class="co1">#display names of objects under the mouse</span>
    libtcod.<span class="me1">console_set_default_foreground</span><span class="br0">(</span>panel, libtcod.<span class="me1">light_gray</span><span class="br0">)</span>
    libtcod.<span class="me1">console_print_ex</span><span class="br0">(</span>panel, <span class="nu0">1</span>, <span class="nu0">0</span>, libtcod.<span class="me1">BKGND_NONE</span>, libtcod.<span class="me1">LEFT</span>, get_names_under_mouse<span class="br0">(</span><span class="br0">)</span><span class="br0">)</span></pre></div></div></div>
<p><br>
But wait! If you recall, in a turn-based game, the rendering is done only once per turn; the rest of the time, the game is blocked on <i>console_wait_for_keypress</i>. During this time (which is <i>most</i> of the time) the code we wrote above would simply not be processed! We switched to real-time rendering by replacing the <i>console_wait_for_keypress</i> call in <i>handle_keys</i> with the <i>sys_check_for_event</i> in the main loop.
</p><p><br>
Won't our game stop being turn-based then? It's funny, but surprisingly it won't! Before you question logic itself, let me tell you that we did some changes earlier that had the side-effect of enabling this.
</p><p>When the player doesn't take a turn (doesn't press a movement/attack key), <i>handle_keys</i> returns a special string (<i> 'didnt-take-turn' </i>). You'll notice that the main loop only allows enemies to take their turns if the value returned from <i>handle_keys</i> is not <i> 'didnt-take-turn' </i>! The main loop goes on, but the monsters don't move. The only real distinction between a real-time game and a turn-based game is that, in a turn-based game, the monsters wait until the player moves to make their move. Makes sense!
</p><p>If you hadn't before, you now need to call <i>libtcod.sys_set_fps(LIMIT_FPS)</i> before the main loop to limit the game's speed. You'll also probably find the movement keys respond too rapidly for a turn-based game where every key press should count as only one turn (see the earlier discussion about this in <a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_1#Moving_around" title="Complete Roguelike Tutorial, using python+libtcod, part 1">Part 1</a>). Just replace the calls to <i>console_is_key_pressed</i> in <i>handle_keys</i> with calls like:
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">        <span class="kw1">if</span> key.<span class="me1">vk</span> == libtcod.<span class="me1">KEY_UP</span>:</pre></div></div></div>
<p><br>
And similarly for the other movement keys. That's it! You can move the mouse around to quickly know the names of every object in sight.
</p><p><br>
The whole code is available <a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7_code" title="Complete Roguelike Tutorial, using python+libtcod, part 7 code">here</a>.
</p><p><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_8" title="Complete Roguelike Tutorial, using python+libtcod, part 8">Go on to the next part</a>.
</p>
<!-- 
NewPP limit report
Preprocessor node count: 185/1000000
Post-expand include size: 0/2097152 bytes
Template argument size: 0/2097152 bytes
Expensive parser function count: 0/100
-->

<!-- Saved in parser cache with key 133099-basin:pcache:idhash:3814-0!*!0!!en!*!* and timestamp 20131214165726 -->
</div><div class="printfooter">
Retrieved from "<a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7&oldid=30255">http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7&amp;oldid=30255</a>"</div>
		<div id="catlinks" class="catlinks"><div id="mw-normal-catlinks" class="mw-normal-catlinks"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:Categories" title="Special:Categories">Category</a>: <ul><li><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Category:Developing" title="Category:Developing">Developing</a></li></ul></div></div>		<!-- end content -->
				<div class="visualClear"></div>
	</div>
</div></div>
<div id="column-one">
	<div id="p-cactions" class="portlet">
		<h5>Views</h5>
		<div class="pBody">
			<ul>
				<li id="ca-nstab-main" class="selected"><a href="./Complete Roguelike Tutorial, using python+libtcod, part 7 - RogueBasin_files/Complete Roguelike Tutorial, using python+libtcod, part 7 - RogueBasin.html" primary="1" context="subject" title="View the content page [alt-c]" accesskey="c">Page</a></li>
				<li id="ca-talk" class="new"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Talk:Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7&action=edit&redlink=1" primary="1" context="talk" title="Discussion about the content page [alt-t]" accesskey="t">Discussion</a></li>
				<li id="ca-viewsource"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7&action=edit" primary="1" title="This page is protected.
You can view its source [alt-e]" accesskey="e">View source</a></li>
				<li id="ca-history"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7&action=history" rel="archives" title="Past revisions of this page [alt-h]" accesskey="h">History</a></li>
			</ul>
		</div>
	</div>
	<div class="portlet" id="p-personal">
		<h5>Personal tools</h5>
		<div class="pBody">
			<ul>
				<li id="pt-login"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:UserLogin&returnto=Complete+Roguelike+Tutorial%2C+using+python%2Blibtcod%2C+part+7" title="You are encouraged to log in; however, it is not mandatory [alt-o]" accesskey="o">Log in / create account</a></li>
			</ul>
		</div>
	</div>
	<div class="portlet" id="p-logo">
<a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Main_Page" style="background-image: url(/skins/roguebasin/RogueBasin_logo.png);" title="Visit the main page"></a>
	</div>
	<div class="generated-sidebar portlet" id="p-navigation">
		<h5>Navigation</h5>
		<div class="pBody">
			<ul>
				<li id="n-mainpage"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Main_Page" title="Visit the main page [alt-z]" accesskey="z">Main Page</a></li>
				<li id="n-portal"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=RogueBasin:Community_portal" title="About the project, what you can do, where to find things">Community portal</a></li>
				<li id="n-recentchanges"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:RecentChanges" title="A list of recent changes in the wiki [alt-r]" accesskey="r">Recent changes</a></li>
				<li id="n-randompage"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:Random" title="Load a random page [alt-x]" accesskey="x">Random page</a></li>
				<li id="n-help"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Help" title="The place to find out">Help</a></li>
			</ul>
		</div>
	</div>
	<div id="p-search" class="portlet">
		<h5><label for="searchInput">Search</label></h5>
		<div id="searchBody" class="pBody">
			<form action="http://roguebasin.roguelikedevelopment.org/index.php" id="searchform">
				<input type="hidden" name="title" value="Special:Search">
				<input type="search" name="search" title="Search RogueBasin [alt-f]" accesskey="f" id="searchInput">
				<input type="submit" name="go" value="Go" title="Go to a page with this exact name if exists" id="searchGoButton" class="searchButton">&nbsp;
				<input type="submit" name="fulltext" value="Search" title="Search the pages for this text" id="mw-searchButton" class="searchButton">
			</form>
		</div>
	</div>
	<div class="portlet" id="p-tb">
		<h5>Toolbox</h5>
		<div class="pBody">
			<ul>
				<li id="t-whatlinkshere"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:WhatLinksHere/Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7" title="A list of all wiki pages that link here [alt-j]" accesskey="j">What links here</a></li>
				<li id="t-recentchangeslinked"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:RecentChangesLinked/Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7" title="Recent changes in pages linked from this page [alt-k]" accesskey="k">Related changes</a></li>
				<li id="t-specialpages"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:SpecialPages" title="A list of all special pages [alt-q]" accesskey="q">Special pages</a></li>
				<li><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7&printable=yes" rel="alternate">Printable version</a></li>
				<li id="t-permalink"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_7&oldid=30255" title="Permanent link to this revision of the page">Permanent link</a></li>
			</ul>
		</div>
	</div>
</div><!-- end of the left (by default at least) column -->
<div class="visualClear"></div>
<div id="footer">
	<div id="f-poweredbyico">
		<a href="http://www.mediawiki.org/"><img src="./Complete Roguelike Tutorial, using python+libtcod, part 7 - RogueBasin_files/poweredby_mediawiki_88x31.png" alt="Powered by MediaWiki" width="88" height="31"></a>
	</div>
	<ul id="f-list">
		<li id="lastmod"> This page was last modified on 13 November 2012, at 04:59.</li>
		<li id="viewcount">This page has been accessed 12,284 times.</li>
		<li id="privacy"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=RogueBasin:Privacy_policy" title="RogueBasin:Privacy policy">Privacy policy</a></li>
		<li id="about"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=RogueBasin:About" title="RogueBasin:About">About RogueBasin</a></li>
		<li id="disclaimer"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=RogueBasin:General_disclaimer" title="RogueBasin:General disclaimer">Disclaimers</a></li>
	</ul>
</div>
</div>
<script>if(window.mw){
mw.loader.load(["mediawiki.user","mediawiki.page.ready"], null, true);
}</script><script src="./Complete Roguelike Tutorial, using python+libtcod, part 7 - RogueBasin_files/load(3).php" type="text/javascript"></script>
<script src="./Complete Roguelike Tutorial, using python+libtcod, part 7 - RogueBasin_files/load(4).php"></script>
<!-- Served in 0.200 secs. --><autoscroll_cursor hidden=""></autoscroll_cursor></body></html>